package org.marverse.universe

import java.lang.ref.WeakReference
import java.util.UUID
import scala.collection.Map
import scala.collection.mutable.HashSet
import util.{TypedMap, TypedKey, MutableTypedMap, Checkable}
import persistance.{PId, PersistentStore}
import serialization.{Inst, ObjectInput, ObjectOutput, ClassRegistry}

class Universe private (val persistentStore: PersistentStore, val uuid: UUID) 
extends Checkable {
  
  override def check(implicit ctx: MutableTypedMap[TypedKey, Any]) {
    implicit val ctx2 = ctx.clone
    ctx2(Universe) = this
    extensions.values foreach { _.check(ctx2) }
  }
  
  def advance: Universe = {
    val result = new Universe(persistentStore, uuid)
    for((k,v) <- extensions) {
      result.mutableExtensions.put(k, k.valueClass.cast(v.advance(result)))
    }
    result
  }

  private val mutableExtensions = MutableTypedMap[TypedKey, UniverseExtension]()
  val extensions: TypedMap[TypedKey, UniverseExtension] = mutableExtensions.readOnly
  def apply[T <: UniverseExtension](provider: UniverseExtensionProvider[T]): T = extensions(provider)
}

object Universe extends TypedKey[Universe](classOf[Universe]) {

  final case class AddUniverseExtension[T <: UniverseExtension]  
  (val provider: UniverseExtensionProvider[T]) extends UniverseTransition {
    def check(implicit ctx: MutableTypedMap[TypedKey, Any]) {
      assertThat(!ctx(Universe).extensions.contains(provider),
        "universe extension (%s) already installed".format(provider))
    }
    def perform(universe: Universe) {
      universe.mutableExtensions(provider) = provider.createExtension(universe)
    }
  }
  object AddUniverseExtension {
    object S11n_0 extends Inst[AddUniverseExtension[_ <: UniverseExtension]] {
      def ids = Seq(3)
      def classes = Seq(classOf[AddUniverseExtension[_ <: UniverseExtension]])
      def instantiate(in: ObjectInput, id: ClassRegistry#ID, ctx: MutableTypedMap[TypedKey, Any]) =  
        new AddUniverseExtension(in.read(classOf[UniverseExtensionProvider[_ <: UniverseExtension]])(ctx))
      def serializeInstance(that: AddUniverseExtension[_ <: UniverseExtension], out: ObjectOutput) {
        out.write(that.provider)
      }
    }  
  }
  
  def apply(persistentStore: PersistentStore) = new Universe(persistentStore, UUID.randomUUID)
  lazy val S11n = Seq(AddUniverseExtension.S11n_0)
}
  
